<?php

namespace app\worker\controller\kefu;

use think\App;
use Throwable;
use think\facade\Db;
use GatewayWorker\Lib\Gateway;
use app\admin\model\kefu\User;
use app\admin\model\kefu\Session;
use app\worker\library\kefu\Common;
use app\worker\library\WorkerWsApp;
use app\admin\model\kefu\QuickReply;
use app\common\library\Auth as UserAuth;
use app\admin\library\Auth as AdminAuth;
use app\worker\library\kefu\Auth as KeFuAuth;
use app\common\library\token\TokenExpirationException;

/**
 * 客服 WebSocket Index 控制器
 */
class WsIndex
{
    /**
     * 客服系统配置
     */
    protected array $config = [];

    /**
     * 构造方法
     * @param WorkerWsApp $app 应用对象
     * @throws Throwable
     */
    public function __construct(protected App $app)
    {
        $configData = Db::name('kefu_config')->select();
        foreach ($configData as $item) {
            $this->config[$item['name']] = $item['value'];
        }

        $this->app->lang->load([
            app_path() . 'lang' . DIRECTORY_SEPARATOR . 'date.php',
        ]);
    }

    /**
     * Im的初始化方法
     * @throws Throwable
     */
    public function initialize(): bool
    {
        $baToken        = $this->app->message['baToken'] ?? ''; // 管理token
        $baUserToken    = $this->app->message['baUserToken'] ?? ''; // 会员token
        $baTouristToken = $this->app->message['baTouristToken'] ?? ''; // 游客token
        $backend        = $this->app->message['backend'] ?? false; // 是否是在后台

        // 本次初始化之前的用户信息（一个连接可能经历两次初始化）
        $beforeInitUserInfo = Common::getUser();
        if ($beforeInitUserInfo) {
            Gateway::leaveGroup($this->app->clientId, Common::mark('touristGroup'));
            Gateway::leaveGroup($this->app->clientId, Common::mark('userGroup'));
            Gateway::leaveGroup($this->app->clientId, Common::mark('csrGroup'));

            // 游客离线
            if ($beforeInitUserInfo['identity'] == 'tourist') {
                Gateway::sendToGroup(Common::mark('csrGroup'), $this->app->assembleMessage('tourists', [
                    'action' => 'remove',
                    'user'   => $beforeInitUserInfo,
                ]));
            }
        }

        // 客服系统的用户验权类实例
        $keFuAuth = KeFuAuth::instance();

        try {
            // 管理员转IM用户
            if ($baToken) {
                $auth = AdminAuth::instance();
                $auth->init($baToken);

                if ($auth->isLogin()) {
                    $keFuAuth->kfUser('admin', $auth->getAdmin()->toArray() + ['token' => $baTouristToken]);
                }
            }

            // 会员转IM用户
            if (!$keFuAuth->isLogin() && $baUserToken) {
                $auth = UserAuth::instance();
                $auth->init($baUserToken);

                if ($auth->isLogin()) {
                    $keFuAuth->kfUser('user', $auth->getUser()->toArray() + ['token' => $baTouristToken]);
                }
            }

            // 游客
            if (!$keFuAuth->isLogin() && $this->config['tourist_switch'] == 'yes') {
                $keFuAuth->kfUser('tourist', ['token' => $baTouristToken]);

                // 开启了游客，但任然未能初始化系统用户
                if (!$keFuAuth->isLogin()) {
                    $this->app->send('error', [
                        'message'         => '未能识别身份，请重新登录！',
                        'code'            => 500,
                        'closeConnection' => true,
                        'duration'        => 0,
                    ]);
                    return false;
                }
            }
        } catch (TokenExpirationException) {
        }

        if (!$keFuAuth->isLogin()) {
            Gateway::closeCurrentClient($this->app->assembleMessage('init', [
                'imUser'  => false,
                'message' => Common::appMsg('无可用身份进入系统'),
            ]));
            return false;
        }

        // 后台是否启动系统
        if ($backend && ($this->config['backend_start'] == 'no' || ($this->config['backend_start'] == 'csr_start' && $keFuAuth->identity != 'csr'))) {
            $this->app->send('close', [
                'message' => Common::appMsg('已配置后台不启动，系统自动关闭中...'),
            ]);
            return false;
        }

        // UID、用户态
        $kfUser = $keFuAuth->getUser()->toArray();
        Gateway::bindUid($this->app->clientId, Common::mark('uid', $kfUser)); // 绑定uid
        Common::setUser($kfUser); // 设置用户登录态

        // 是否存在有效会话
        $session = Db::name('kefu_session')
            ->where('user_id', $kfUser['id'])
            ->whereNull('delete_time')
            ->order([
                'sort_time'   => 'desc',
                'create_time' => 'desc',
            ])
            ->find();

        // IM用户初始化信息组装
        $status = Common::changeUserStatus('online');
        $imUser = [
                'status'        => $status,
                'session'       => $session && $session['session_user_id'] > 0 ? $session['id'] : false,
                'unreadMessage' => Common::getUnreadMessage($kfUser['id']),
            ] + $keFuAuth->getUserInfo();

        // IM用户初始化信息发送
        $this->app->send('init', [
            'imUser' => $imUser,
        ]);

        // 可以广播的数据 - 状态、ID号、身份标识
        $onlineUserInfo = ['status' => $status] + Common::getUser();

        // 客服
        if ($kfUser['identity'] == 'csr') {
            Gateway::joinGroup($this->app->clientId, Common::mark('csrGroup'));
            Gateway::sendToAll($this->app->assembleMessage('changeStatus', $onlineUserInfo), null, [$this->app->clientId]);

            // 游客列表
            $touristIds = [];
            $tourists   = Gateway::getClientSessionsByGroup(Common::mark('touristGroup'));
            foreach ($tourists as $tourist) {
                $touristIds[] = $tourist[Common::mark('sessionKey')]['id'];
            }

            $this->app->send('csrWindowInit', [
                'tourists'   => Common::getTourists($touristIds),
                'sessions'   => Common::getSessions($kfUser['id']),
                'quickReply' => QuickReply::where('user_id', 'in', [$kfUser['id'], 0])
                    ->where('status', 1)
                    ->order('weigh', 'desc')
                    ->select(),
            ]);
            return true;
        }

        // 用户
        $href = request()->get('href');
        if ($href) {
            Db::name('kefu_trajectory')->insert([
                'user_id'     => $imUser['id'],
                'title'       => '访问',
                'url'         => $href,
                'referrer'    => request()->get('referrer', request()->header('origin')),
                'create_time' => time(),
            ]);
        }
        if ($imUser['session']) {
            // 有会话的用户上线

            // 加入分组
            Gateway::joinGroup($this->app->clientId, Common::mark('userGroup'));

            // 通知他的客服代表用户上线
            Gateway::sendToUid(Common::mark('uid', [
                'id'       => $session['session_user_id'],
                'identity' => 'csr',
            ]), $this->app->assembleMessage('changeStatus', $onlineUserInfo));
        } else {
            // 无会话的用户上线 - 推送给所有客服代表
            Gateway::joinGroup($this->app->clientId, Common::mark('touristGroup'));
            Gateway::sendToGroup(Common::mark('csrGroup'), $this->app->assembleMessage('tourists', [
                'action' => 'list',
                'data'   => Common::getTourists([$kfUser['id']]),
            ]), [$this->app->clientId]);
        }

        // 清理轨迹
        if ($this->config['trajectory_save']) {
            switch ($this->config['trajectory_save']) {
                case 7:
                    $whereTime = 604800;
                    break;
                case 30:
                    $whereTime = 2592000;
                    break;
                case 90:
                    $whereTime = 7776000;
                    break;
                case 180:
                    $whereTime = 15552000;
                    break;
            }
            if (!empty($whereTime)) {
                Db::name('kefu_trajectory')->where('create_time', '<=', time() - $whereTime)->delete();
            }
        }
        return true;
    }

    /**
     * 为无会话的访客分配客服代表
     * @throws Throwable
     */
    public function distribution(): void
    {
        Common::getSession($this->app->clientId);

        $kfUser = Common::getUser();

        // 是否存在有效会话
        $session = Db::name('kefu_session')
            ->where('user_id', $kfUser['id'])
            ->whereNull('delete_time')
            ->order([
                'sort_time'   => 'desc',
                'create_time' => 'desc',
            ])
            ->find();
        if ($session) {
            $this->app->send('distribution', [
                'session' => $session,
            ]);
            return;
        }

        // 客服代表团队的满载情况
        $csrList = Db::name('kefu_user')
            ->field('id,identity,csr_maximum_reception,csr_reception_count')
            ->where('identity', 'csr')
            ->where('status', 'online')
            ->select();
        $csrFull = true;
        foreach ($csrList as $csr) {
            // 存在未满载的客服代表
            if ($csr['csr_reception_count'] < $csr['csr_maximum_reception']) {
                $csrFull = false;
                break;
            }
        }

        $csrCommonWhere = [
            'identity' => 'csr',
            'status'   => 'online',
        ];

        if ($this->config['csr_distribution'] == 'manual' || ($this->config['csr_distribution_full_manual'] == 'yes' && $csrFull)) {
            // 客服代表手动接入
            $csrId = 0;
        } elseif ($this->config['csr_distribution'] == 'intensity') {
            // 自动按工作强度分配 - 当前接待量最少的客服
            $csrId = Db::name('kefu_user')
                ->where($csrCommonWhere)
                ->order([
                    'csr_reception_count'     => 'asc',
                    'csr_last_reception_time' => 'asc',
                ])
                ->value('id');
        } elseif ($this->config['csr_distribution'] == 'ability') {
            // 自动按工作能力分配 - 根据接待上限和当前接待量，分配给最能接待的客服
            $csrUser = Db::name('kefu_user')
                ->field('id,CAST(csr_maximum_reception as signed) - CAST(csr_reception_count as signed) as weight')
                ->where($csrCommonWhere)
                ->order('weight', 'desc')
                ->find();
            if ($csrUser) {
                $csrId = $csrUser['id'];
            }
        }

        if ($this->config['csr_distribution'] == 'turns' || !isset($csrId)) {
            // 自动轮流分配 - 分配给最久未进行接待的客服
            $csrId = Db::name('kefu_user')
                ->where($csrCommonWhere)
                ->order('csr_last_reception_time', 'asc')
                ->value('id');
        }

        $session = Session::create([
            'user_id'         => $kfUser['id'],
            'session_user_id' => !empty($csrId) ? $csrId : 0,
            'type'            => 'single',
            'sort_time'       => time(),
        ]);

        $this->app->send('distribution', [
            'session' => $session,
        ]);

        if (!empty($csrId)) {
            // 广播：访客已被接待
            $sessionUserInfo = User::where('id', $kfUser['id'])->find();
            $sessionUserInfo = User::outputFields(['id', 'identity', 'avatar', 'nickname', 'status', 'last_login_time', 'create_time'], $sessionUserInfo);
            $clientIds       = Gateway::getClientIdByUid(Common::mark('uid', $sessionUserInfo));
            foreach ($clientIds as $clientId) {
                Gateway::leaveGroup($clientId, Common::mark('touristGroup'));
                Gateway::joinGroup($clientId, Common::mark('userGroup'));
            }
            Gateway::sendToGroup(Common::mark('csrGroup'), $this->app->assembleMessage('tourists', [
                'action' => 'reception',
                'user'   => $sessionUserInfo,
            ]));

            // 发送欢迎消息
            $csrWelcomeMsg = Common::trueValue([
                Db::name('kefu_user')->where('id', $csrId)->value('csr_welcome_msg'),
                $this->config['new_user_msg'],
            ]);
            if ($csrWelcomeMsg) {
                Common::sendSessionMessage($session->id, 'text', $csrWelcomeMsg, [], $csrId);
            }
        }
    }

    /**
     * 修改状态
     * @throws Throwable
     */
    public function changeStatus(): void
    {
        $status = $this->app->message['status'] ?? '';
        if ($status) {
            $kfUser    = Common::getUser();
            $newStatus = Common::changeUserStatus($status, true);
            $uid       = Common::mark('uid', $kfUser);

            // 发送给自己的
            $this->app->send('changeStatus', ['status' => $newStatus] + $kfUser, $uid);

            // 新状态是隐身
            if ($newStatus == 'invisible') $newStatus = 'offline';

            // 将客服代表的新状态发送给所有人
            Gateway::sendToAll($this->app->assembleMessage('changeStatus', ['status' => $newStatus] + Common::getUser()), null, Gateway::getClientIdByUid($uid));
        }
    }
}